﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using QuickGraph;
using GraphSharp.Controls;
using System.ComponentModel;

namespace VirtualOperator
{
    public class VirtualNetwork : BidirectionalGraph<VirtualNetwork.Matrix, VirtualNetwork.Link>
    {
        public class Matrix
        {
            public int ID {get; set;}
            public int ManagerID { get;private set; }
            public string Name  {get; private set;}
            public string Type  {get; private set;}
            public int Delay { get; set; }
            private Dictionary<int, NwTTP> nwTTPs;
            public Dictionary<int, NwTTP> NwTTPs { get { return nwTTPs; } }
            public TransportLayer.NodeToolsLibrary.Point Possition;
            public bool Open{get; set;}
            public Matrix(int managerId, string name, string type, TransportLayer.NodeToolsLibrary.Point point)
            {
                ManagerID = managerId;
                ID = 0;
                Name = name;
                Type = type;
                Possition = point;
                nwTTPs = new Dictionary<int, NwTTP>();
            }
         
            public override string ToString()
            {
                    return Name;
            }

            public Matrix Clone(int newId, int delay)
            {
                Matrix matrix = new Matrix(ManagerID, Name, Type, Possition.Clone());
                matrix.Delay = delay;
                matrix.ID = newId;
                return matrix;
            }
        }



        public class Link : Edge<Matrix>
        {
       
            public int Capacity;
            public int MaxCapacity { get; private set; }
            public int Id { get; private set; }
            private int delay;
            private float errorRate;
            public int Delay { get { return delay; } set { delay = value; } }
            public float ErrorRate { get { return errorRate; } set { errorRate = value; } }
            public bool Open { get; set; }
            public Link(int id, VirtualNetwork.Matrix source, VirtualNetwork.Matrix target, int capacity): base(source, target)
            {
                this.Id = id;
                this.Capacity = this.MaxCapacity = capacity;
            }

            public override string ToString()
             {
                return this.Source.Name +"->"+ this.Target.Name + " ,Capacity: [" + this.Capacity + "/" + this.MaxCapacity + "]";
            }

        }
        private AddNewNwTTP AddNewNwTTPHandler = null;
        private Communicator communicator;
        private RoutingController rc;
        private LogMessages log;
        public int ID {get; private set;}
        public string Name { get; set; }

        public int DelayWeight { get; private set; }
        public int UsageWeight { get; private set; }
        public int ErrorRateWeight { get; private set; }

        public Dictionary<int, UserConnection> UserConnectionsDictonary = new Dictionary<int, UserConnection>();
        public Dictionary<int, Matrix> MatrixesDictonary = new Dictionary<int, Matrix>();
        //bindings
        private BindingList<Matrix> bindingMatrixes = new BindingList<Matrix>();
        private BindingList<Link> bindingLinks = new BindingList<Link>();
        private BindingList<UserConnection> bindingUsherConnections = new BindingList<UserConnection>();
        private BindingList<NwTTP> bindingUsers = new BindingList<NwTTP>();
 
        public CallController CC;

        //matrix id --> Nwttp
        private Dictionary<NwTTP, VirtualNetwork.Matrix> nwTTPDictonary;
        public Dictionary<NwTTP, VirtualNetwork.Matrix> NwTTPDictonary { get { return nwTTPDictonary; } }
        //if exist
        public BindingList<Link> Links { get { return bindingLinks; } }
        public BindingList<Matrix> Matrixes { get { return bindingMatrixes; } }
        public BindingList<UserConnection> UserConnections { get { return bindingUsherConnections; } }
        public BindingList<NwTTP> Users { get { return bindingUsers; } }
   



        public Link IsBidirectionalLink(Link link)
        {
            foreach (Link eg in Edges)
            {
                if (link.Source == eg.Target && link.Target == eg.Source)
                    return eg;
            
            }
            return null;
        }


        public int GetUserConnectionFreeID()
        {
            int index = 1;
            while (UserConnectionsDictonary.ContainsKey(index))
            {
                index++;
            }

            return index;
        }

        public VirtualNetwork.Matrix this[int index]
        {
            get { return MatrixesDictonary[index]; }
        }


        public bool AddMatrix(VirtualNetwork.Matrix v)
        {
            if (base.AddVertex(v))
            {
                this.MatrixesDictonary.Add(v.ID, v);
                this.bindingMatrixes.Add(v);
                return true;
            }
            return false;
        }

        public void RemoveMatrix(VirtualNetwork.Matrix v)
        {
            base.RemoveVertex(v);
            MatrixesDictonary.Remove(v.ID);
            bindingMatrixes.Remove(v);

        }

        public void RemoveLink(VirtualNetwork.Link e)
        {
            base.RemoveEdge(e);
            RemoveVertex(e.Source);
            RemoveVertex(e.Target);
            bindingLinks.Remove(e);
            log.AppendLog(Task.REMOVE_VIRTUAL_LINK, ID.ToString() ,"OK" + e.ToString());
            communicator.RemoveVirtualLink(e.Source.ID, e.Target.ID);
        
        }
        public void AddNwTTP(NwTTP nwttp, int matrixID)
        {
            if (MatrixesDictonary.ContainsKey(matrixID))
            {
                MatrixesDictonary[matrixID].NwTTPs.Add(nwttp.ID, nwttp);
                nwTTPDictonary.Add(nwttp, MatrixesDictonary[matrixID]);
                bindingUsers.Add(nwttp);
                CC.AddNwTTP(nwttp, nwttp.Port);
            }
        }

        public void RemoveNwTTP(NwTTP nwttp)
        {
           if(nwTTPDictonary.ContainsKey(nwttp))
           {
               nwTTPDictonary[nwttp].NwTTPs.Remove(nwttp.ID);
               nwTTPDictonary.Remove(nwttp);
               communicator.RemoveSinkSource(ID, nwttp.ID);
               bindingUsers.Remove(nwttp);
               if (nwttp.Type == Direction.SINK)
                   log.AppendLog(Task.REMOVE_SINK, ID.ToString(), "OK");
               else
                   log.AppendLog(Task.REMOVE_SOURCE, ID.ToString(), "OK");
           }
        }

        public bool AddLink(VirtualNetwork.Link e)
        {
            if (base.AddEdge(e))
            {
                bindingLinks.Add(e);
                return true;
            }
            else
                return false;
        }

        public bool AddLink(VirtualNetwork.Matrix source, VirtualNetwork.Matrix target, int capacity, int maxDelay, float linkErrorRate)
        {
            int srcOut = 0;
            int trgOut = 0;
            int linkId = 0;
            if (communicator.SetVirtualLink(ID, source.ManagerID, target.ManagerID, maxDelay, linkErrorRate, capacity, out srcOut, out trgOut, out linkId))
            {

                if (!MatrixesDictonary.ContainsKey(srcOut))
                    this.AddMatrix(source.Clone(srcOut, maxDelay));
                if (!MatrixesDictonary.ContainsKey(trgOut))
                    this.AddMatrix(target.Clone(trgOut, maxDelay));

                Link link = new Link(linkId, MatrixesDictonary[srcOut], MatrixesDictonary[trgOut], capacity);
                link.ErrorRate = linkErrorRate;
                link.Delay = maxDelay;
                if (base.AddEdge(link))
                {
                    bindingLinks.Add(link);
                    return true;
                }
                else
                    return false;
            }
            else return false;
        }
        public bool TeardownConnection(int connectionID)
        {
            if (UserConnectionsDictonary.ContainsKey(connectionID))
            {
                CC.TeardownCall(UserConnectionsDictonary[connectionID]);
                communicator.Teardown(UserConnectionsDictonary[connectionID], this.ID);
           
                bindingUsherConnections.Remove(UserConnectionsDictonary[connectionID]);
                UserConnectionsDictonary[connectionID].RemovePath();
                UserConnectionsDictonary.Remove(connectionID);
                
                
                return true;
            }
            else
                return false;

        }
        //niezle zaabezpieczone us-userconnection nc - networkconnection
        public UserConnection SetupConnection(NwTTP srcNwTTP, NwTTP trgNwTTP, int capicity)
        {
            UserConnection uc = null;

            if (nwTTPDictonary.ContainsKey(srcNwTTP) && nwTTPDictonary.ContainsKey(trgNwTTP) && srcNwTTP.Type == Direction.SOURCE && trgNwTTP.Type == Direction.SINK)
            {
                List<Link> nc;
                nc = (RoutingController.setupConnection(this, nwTTPDictonary[srcNwTTP].ID, nwTTPDictonary[trgNwTTP].ID, capicity));

                if (nc != null)
                {
                    uc = new UserConnection(srcNwTTP, trgNwTTP, nc, capicity);
                    uc.ID = communicator.Connect(uc, this.ID);
                    if (uc.ID > 0)
                    {
                        UserConnectionsDictonary.Add(uc.ID, uc);
                        uc.SetPath();
                        bindingUsherConnections.Add(uc);
                        //CC.BeginCall(srcNwTTP.Name, trgNwTTP.Name, uc);
                        //log.AppendLog(Task.SETUP_CONNECTION, Name, uc.ToString());

                    }
                    else uc = null;
                   
                }
               
            }

            return uc;

        }

        public bool ContainsNode(int id)
        {
            return MatrixesDictonary.ContainsKey(id);
        }


        public List<NwTTP> Sinks 
        { 
            get{
                List<NwTTP> sinks = new List<NwTTP>();
                foreach (NwTTP sink in nwTTPDictonary.Keys)
                    {
                        if (sink.Type == Direction.SINK)
                            sinks.Add(sink);
                    }
                    return sinks;
               }
        }

        public List<NwTTP> Sources
        {
            get
            {
                List<NwTTP> sources = new List<NwTTP>();
                foreach (NwTTP source in nwTTPDictonary.Keys)
                {
                    if (source.Type == Direction.SOURCE)
                        sources.Add(source);
                }
                return sources;
            }
        }


        public override string ToString()
        {
            return this.Name+"_"+this.ID;
        }
        public VirtualNetwork(Communicator communicator, RoutingController rc,int id, string name, int delay, int usage, int errorRate, string vnoName)
            : base() 
        {
            this.communicator = communicator;
            this.rc = rc;
            this.ID = id; 
            CC = new CallController(this, vnoName);
            Name = name;
            DelayWeight = delay;
            ErrorRateWeight = errorRate;
            UsageWeight = usage;
            nwTTPDictonary = new Dictionary<NwTTP, VirtualNetwork.Matrix>();
            log = LogMessages.GetInstance();

        }

        public VirtualNetwork(int id, string name, int delay, int usage, int errorRate, string vnoName)
            : base()
        {
            
            this.ID = id;
            CC = new CallController(this, vnoName);
            Name = name;
            DelayWeight = delay;
            ErrorRateWeight = errorRate;
            UsageWeight = usage;
            nwTTPDictonary = new Dictionary<NwTTP, VirtualNetwork.Matrix>();
        }


        public VirtualNetwork() : base()
        { }

        public VirtualNetwork(bool allowParallelEdges)
            : base(allowParallelEdges) { }

        public VirtualNetwork(bool allowParallelEdges, int vertexCapacity)
            : base(allowParallelEdges, vertexCapacity) { }

        public VirtualNetwork Clone()
        {
            VirtualNetwork topology = new VirtualNetwork();

            topology.AddVertexRange(this.Vertices);
            topology.AddEdgeRange(this.Edges);

            return topology;

        }
    }
}
